/************************************************************************************************************
 *
 * This class defines all the <code>VInst</code>, a <code>VInst</code> contains : 
 *   Opcode : like add , sub
 *   Opkind : the category of Opcode
 *   Operand :  the operands of a vinst, the number of operands vary according to the <code>Opcode</code>
 *
 ***********************************************************************************************************/
// VINST.h
// This file contains the definition of intermediate representation
#ifndef VINST_H
#define VINST_H

#include "crossbit/common.h"
#include <iostream>
#include <vector>

#include <assert.h>
#include <stdarg.h>

namespace crossbit {

    // -------------------------------------------------------------------
    //	    Shortcuts for creating virtual instructions
    // -------------------------------------------------------------------

#define PUT(a, b)		(VInst2::createPUT((a), (b)))
#define GET(a, b)		(VInst2::createGET((a), (b)))

#define LD(a, b, c, d)		(VInst4::createLD((a), (b), (c), (d)))
#define ST(a, b, c, d)		(VInst4::createST((a), (b), (c), (d)))

#define MOV(a, b)		(VInst2::createMOV((a), (b)))
#define LI(a, b)		(VInst2::createLI((a), (b)))
    // added on 2009-2-14 , by stt
#define LIRETURN(a, b)  (VInst2::createLIRETURN((a), (b)))

#define SEXT(a, b, c)		(VInst3::createSEXT((a), (b), (c)))
#define ZEXT(a, b, c)		(VInst3::createZEXT((a), (b), (c)))
#define ADD(a, b, c)		(VInst3::createADD((a), (b), (c)))
#define ADDI(a, b, c)		(VInst3::createADDI((a), (b), (c)))
#define SUB(a, b, c)		(VInst3::createSUB((a), (b), (c)))
#define AND(a, b, c)		(VInst3::createAND((a), (b), (c)))
#define OR(a, b, c)		(VInst3::createOR((a), (b), (c)))
#define XOR(a, b, c)		(VInst3::createXOR((a), (b), (c)))
#define NOT(a, b)		(VInst2::createNOT((a), (b)))
#define MUL(a, b, c, d)		(VInst4::createMUL((a), (b), (c), (d)))
#define MULU(a, b, c, d)	(VInst4::createMULU((a), (b), (c), (d)))
#define DIV(a, b, c, d)		(VInst4::createDIV((a), (b), (c), (d)))
#define DIVU(a, b, c, d)	(VInst4::createDIVU((a), (b), (c), (d)))
#define SLL(a, b, c)		(VInst3::createSLL((a), (b), (c)))
#define SRL(a, b, c)		(VInst3::createSRL((a), (b), (c)))
#define SRA(a, b, c)		(VInst3::createSRA((a), (b), (c)))
    //#define SLLV(a, b, c)		(VInst3::createSLLV((a), (b), (c)))
    //#define SRLV(a, b, c)		(VInst3::createSRLV((a), (b), (c)))
    //#define SRAV(a, b, c)		(VInst3::createSRAV((a), (b), (c)))
#define CMP(a, b, c, d)		(VInst4::createCMP((a), (b), (c), (d)))

#define JMP(a, b)		(VInst2::createJMP((a), (b)))
#define RETURN(a, b)		(VInst2::createRETURN((a), (b)))
#define BRANCH(a, b, c, d, e)	(VInst5::createBRANCH((a), (b), (c), (d), (e)))

#define HALT(a)			(VInst0::createHALT(a))
#define SYSCALL			(VInst0::createSYSCALL())
#define CALL(a, b, ...)		(VInstVar::createCALL((a),(b),  __VA_ARGS__))

    // Float Point Instructions

#define FPUTS(a)		(VInst1::createFPUTS((a)))
#define FPUTD(a)		(VInst1::createFPUTD((a)))
#define FGETS(a)		(VInst1::createFGETS((a)))
#define FGETD(a)		(VInst1::createFGETD((a)))

#define FLD(a,b,c,d)		(VInst4::createFLD((a),(b),(c),(d)))
#define FST(a,b,c,d)		(VInst4::createFST((a),(b),(c),(d)))

#define FMOV(a,b)		(VInst2::createFMOV((a),(b)))

#define FADDS()			(VInst0::createFADDS())
#define FADDD()			(VInst0::createFADDD())
#define FSUBS()			(VInst0::createFSUBS())
#define FSUBD()			(VInst0::createFSUBD())
#define FMULS()			(VInst0::createFMULS())
#define FMULD()			(VInst0::createFMULD())
#define FDIVS()			(VInst0::createFDIVS())
#define FDIVD()			(VInst0::createFDIVD())
#define FNEGS()			(VInst0::createFNEGS())
#define FNEGD()			(VInst0::createFNEGD())
#define FABSS()			(VInst0::createFABSS())
#define FABSD()			(VInst0::createFABSD())
#define FSQRTS()		(VInst0::createFSQRTS())
#define FSQRTD()		(VInst0::createFSQRTD())
#define FCMPS(a,b,c,d)		(VInst4::createFCMPS((a),(b),(c),(d)))
#define FCMPD(a,b,c,d)		(VInst4::createFCMPD((a),(b),(c),(d)))

#define FCONV(a,b,c,d)		(VInst4::createFCONV((a),(b),(c),(d)))
#define FINCSTP()		(VInst0::createFINCSTP())
    // ---------------------------------------------------------------------



    // Compare Condition Code
    enum CmpCC {
        EQ,	// equal
        NE,	// not equal
        GT,	// greater than
        GE,	// greater than or equal
        LT,	// less than
        LE,	// less than or equal
        AB,	// above (unsigned GT)
        BE,	// below (unsigned LT)
        NA,	// not above
    };

    // VInstruction Operand
    typedef struct vinst_operand_t {
        enum operand_type {
            SREG,	// source register
            VREG_USE,   // virtual register that is used
            VREG_DEF,	// virtual register that is defined
            FREG,	// float-point register
            //MEM,
            //PC,
            IMM,
            SIZE,
            CC,
            WORD,
            INVALID,
        } type;

        union {
            // register number (including SREG, VREG_USE, VREG_DEF, FREG)
            XTRegNum reg;

            // immediate value
            XTInt32 imm;

            // BYTE / HALFWORD / WORD 
            BlockSize size;

            // compare condition
            CmpCC	cc;

            // just plain word
            XTInt32 word;
        };

        vinst_operand_t(enum operand_type t)
            : type(t)
            {
            }

        // Dummy anonymous constructor for initialize 
        // operand array
        vinst_operand_t()
            :type(INVALID)
            {
            }

    } Operand;

    enum Opcode {
        OP_INT_BASE = 0x0,  ///< Token to indicate interger inst start

        OP_GET,		OP_PUT,

        OP_LD,		OP_ST,

        OP_MOV,		OP_LI, OP_LIRETURN,

        OP_SEXT,	OP_ZEXT, 
        OP_ADDI,
        OP_ADD,		OP_SUB,	
        OP_AND,		OP_NOT, 
        OP_XOR,		OP_OR, 
        OP_MUL,		OP_MULU, 
        OP_DIV,		OP_DIVU, 
        OP_SLL,		OP_SRL,	    OP_SRA, 
        OP_SLLV,	OP_SRLV,    OP_SRAV,
        OP_CMP,


        OP_JMP, OP_RETURN,	OP_BRANCH,


        OP_HALT,	OP_SYSCALL,
        OP_CALL,
        //total 24 instructions

        OP_INT_END, ///< Token to indicate integer inst ends

        OP_FLOAT_BASE,  ///< Token to indicate float inst start

        //float point instructions
        OP_FPUT_S,	OP_FGET_S,
        OP_FPUT_D,	OP_FGET_D,

        OP_FLD,		OP_FST,

        OP_FMOV,

        OP_FADD_S,	OP_FADD_D,
        OP_FSUB_S,	OP_FSUB_D,
        OP_FMUL_S,	OP_FMUL_D,
        OP_FDIV_S,	OP_FDIV_D,
        OP_FNEG_S,	OP_FNEG_D,
        OP_FABS_S,	OP_FABS_D,
        OP_FSQRT_S,	OP_FSQRT_D,
        OP_FCMP_S,	OP_FCMP_D,

        OP_FLOAT_END,  ///< Token to indicate float inst ends

        OP_FCONV,	// total 24 float-point v-instructions
        OP_FINCSTP,	// added by ZhengJuyu
        OP_TATAL_INST, ///< The quantity of v-instructions
    };	    

    // Auxiliary functions for OpCode Judgement
    inline bool IsIntegerOp(const Opcode& op){
        return (op > OP_INT_BASE && op < OP_INT_END);
    }
    inline bool IsFloatOp(const Opcode& op){
        return (op > OP_FLOAT_BASE && op < OP_FLOAT_END);
    }

    // the category of the <code>Opcode</code>
    enum Opkind {
        OPK_STATEMAP,		// map simulated machine state to/from virtual register
        OPK_MEMORYACCESS,	// load/store
        OPK_DATAMOVEMENT,	// register -> register, immediate -> register
        OPK_COMPUTATION,	// add, sub, and, not, xor, or, mul, div, shift, cmp ...
        OPK_CONTROLTRANSFER,	// jmp
        OPK_SPECIAL,		// any operation that doesn't fit into above categorie
    };

    // Virtual Instruction
    // base class of virtual instruction
    class VInst {
        // printing VInst for debugging purpose
        friend  std::ostream& operator<<(std::ostream&, const VInst&);

        public:
        virtual ~VInst() 
        {
        }

        inline Opcode getOpcode() const
        {
            return op;
        }

        inline Opkind getOpkind() const
        {
            return kind;
        }

        inline XTMemAddr getVAddr() const
        {
            return vaddr;
        }

        virtual Operand getOperand(int i) const = 0;
        virtual void setOperand(int i, Operand oprnd) = 0;
        virtual int getOperandNum() const = 0;

        protected:
        VInst (Opcode opcode, Opkind opkind)
            : op(opcode), kind(opkind)
            {}
        VInst (Opcode opcode, Opkind opkind, XTMemAddr addr)
            : op(opcode), kind(opkind), vaddr(addr)
            {}

        private:
        Opcode op;
        Opkind kind;

        //added by Zheng Juyu
        //The address of the source instruction
        XTMemAddr vaddr;
    };

    // VInst with NO operands
    // Including instructions:
    class VInst0 : public VInst {
        public:
            // Attention: undefined
            Operand getOperand(int i) const 
            { 
                assert(0 && "No Operand Available"); 
                return Operand(); // dummy, shut off warnings
            }

            void setOperand(int i, Operand oprnd)
            {
                return;
            }

            int getOperandNum() const 
            { 
                return 0; 
            }

            static VInst *createHALT(XTMemAddr addr) 
            {
                return new VInst0(OP_HALT, OPK_SPECIAL, addr);
            }

            static VInst *createSYSCALL()
            { 
                return new VInst0(OP_SYSCALL, OPK_SPECIAL);
            }

            // float-point arithmetic v-instructions
            static VInst *createFADDS()
            {
                return new VInst0(OP_FADD_S, OPK_COMPUTATION);
            }
            static VInst *createFADDD()
            {
                return new VInst0(OP_FADD_D, OPK_COMPUTATION);
            }

            static VInst *createFSUBS()
            {
                return new VInst0(OP_FSUB_S, OPK_COMPUTATION);
            }

            static VInst *createFSUBD()
            {
                return new VInst0(OP_FSUB_D, OPK_COMPUTATION);
            }

            static VInst *createFMULS()
            {
                return new VInst0(OP_FMUL_S, OPK_COMPUTATION);
            }

            static VInst *createFMULD()
            {
                return new VInst0(OP_FMUL_D, OPK_COMPUTATION);
            }

            static VInst *createFDIVS()
            {
                return new VInst0(OP_FDIV_S, OPK_COMPUTATION);
            }

            static VInst *createFDIVD()
            {
                return new VInst0(OP_FDIV_D, OPK_COMPUTATION);
            }

            static VInst *createFNEGS()
            {
                return new VInst0(OP_FNEG_S, OPK_COMPUTATION);
            }

            static VInst *createFNEGD()
            {
                return new VInst0(OP_FNEG_D, OPK_COMPUTATION);
            }

            static VInst *createFABSS()
            {
                return new VInst0(OP_FABS_S, OPK_COMPUTATION);
            }

            static VInst *createFABSD()
            {
                return new VInst0(OP_FABS_D, OPK_COMPUTATION);
            }

            static VInst *createFSQRTS()
            {
                return new VInst0(OP_FSQRT_S, OPK_COMPUTATION);
            }

            static VInst *createFSQRTD()
            {
                return new VInst0(OP_FSQRT_D, OPK_COMPUTATION);
            }

            static VInst* createFINCSTP()
            {
                return new VInst0(OP_FINCSTP, OPK_COMPUTATION);
            } 

        protected:
            VInst0 (Opcode op, Opkind kind)
                : VInst (op, kind) {}
            VInst0 (Opcode op, Opkind kind, XTMemAddr addr)
                : VInst (op, kind, addr) {}
    };

    // VInst with ONE operand
    class VInst1 : public VInst {
        public:
            // Attention: only valide when i == 1, otherwise, undefined.
            Operand getOperand(int i) const
            {
                return operand;
            }

            void setOperand(int i, Operand oprnd)
            {
                if (i == 1)
                    operand = oprnd;
            }

            int getOperandNum() const { return 1; }

            // r1: float point register number
            static VInst *createFGETS(XTRegNum fpr1)
            {
                Operand opr1(Operand::FREG);
                opr1.reg = fpr1;
                return new VInst1(OP_FGET_S, OPK_STATEMAP, opr1);
            }

            // r1: float point register number
            static VInst *createFPUTS(XTRegNum fpr1)
            {
                Operand opr1(Operand::FREG);
                opr1.reg = fpr1;
                return new VInst1(OP_FPUT_S, OPK_STATEMAP, opr1);
            }

            // r1: float point register number
            static VInst *createFGETD(XTRegNum fpr1)
            {
                Operand opr1(Operand::FREG);
                opr1.reg = fpr1;
                return new VInst1(OP_FGET_D, OPK_STATEMAP, opr1);
            }

            // r1: float point register number
            static VInst *createFPUTD(XTRegNum fpr1)
            {
                Operand opr1(Operand::FREG);
                opr1.reg = fpr1;
                return new VInst1(OP_FPUT_D, OPK_STATEMAP, opr1);
            }

        protected:
            VInst1 (Opcode op, Opkind kind, Operand opr) 
                : VInst(op, kind), operand(opr) {}

        private:
            Operand operand;
    };

    // VInst with TWO operands
    class VInst2 : public VInst {
        public:
            // Attention: only valid when i >= 1 && i <= 2, otherwise, undefined
            Operand getOperand(int i) const 
            {
                return operand[i - 1];
            }

            void setOperand(int i, Operand oprnd)
            {
                operand[i - 1] = oprnd;
            }

            int getOperandNum() const { return 2; }

            // r1: source s-register number
            // r2: destination v-register number
            static VInst * createGET(XTRegNum r1, XTRegNum r2)
            {
                Operand opr1(Operand::SREG), opr2(Operand::VREG_DEF);
                opr1.reg = r1;
                opr2.reg = r2;
                return new VInst2(OP_GET, OPK_STATEMAP, opr1, opr2);
            }

            // r1: source v-register number
            // r2: destination s-register number
            static VInst * createPUT(XTRegNum r1, XTRegNum r2)
            {
                Operand opr1(Operand::VREG_USE), opr2(Operand::SREG);
                opr1.reg = r1;
                opr2.reg = r2;
                return new VInst2(OP_PUT, OPK_STATEMAP, opr1, opr2);
            }


            // r1: source v-register number
            // r2: destination v-register number
            static VInst * createNOT (XTRegNum r1, XTRegNum r2)
            {
                Operand opr1(Operand::VREG_USE), opr2(Operand::VREG_DEF);
                opr1.reg = r1;
                opr2.reg = r2;
                return new VInst2(OP_NOT, OPK_COMPUTATION, opr1, opr2);
            }

            // r1: source v-register number
            // r2: destination v-register number
            static VInst * createMOV (XTRegNum r1, XTRegNum r2)
            {
                Operand opr1(Operand::VREG_USE), opr2(Operand::VREG_DEF);
                opr1.reg = r1;
                opr2.reg = r2;
                return new VInst2(OP_MOV, OPK_DATAMOVEMENT, opr1, opr2);
            }

            // r1: source float-point register number
            // r2: destination float-point register number
            static VInst * createFMOV (XTRegNum r1, XTRegNum r2)
            {
                Operand opr1(Operand::FREG), opr2(Operand::FREG);
                opr1.reg = r1;
                opr2.reg = r2;
                return new VInst2(OP_FMOV, OPK_DATAMOVEMENT, opr1, opr2);
            }

            // imm: source immediate value
            // reg: destination v-register number
            static VInst * createLI (XTInt32 imm, XTRegNum reg)
            {
                Operand opr1(Operand::IMM), opr2(Operand::VREG_DEF);
                opr1.imm = imm;
                opr2.reg = reg;
                return new VInst2(OP_LI, OPK_DATAMOVEMENT, opr1, opr2);
            }
            static VInst * createLIRETURN (XTInt32 imm, XTRegNum reg)
            {
                Operand opr1(Operand::IMM), opr2(Operand::VREG_DEF);
                opr1.imm = imm;
                opr2.reg = reg;
                return new VInst2(OP_LIRETURN, OPK_DATAMOVEMENT, opr1, opr2);
            }

            // (disp)reg: jump destination, where "type" specifies SPC or TPC.
            static VInst * createJMP(XTRegNum reg, XTInt32 disp)
            {
                Operand opr1(Operand::VREG_USE);
                Operand opr2(Operand::IMM);
                opr1.reg = reg;
                opr2.imm = disp;
                return new VInst2(OP_JMP, OPK_CONTROLTRANSFER, opr1, opr2);
            }
            static VInst * createRETURN(XTRegNum reg, XTInt32 disp)
            {
                Operand opr1(Operand::VREG_USE);
                Operand opr2(Operand::IMM);
                opr1.reg = reg;
                opr2.imm = disp;
                return new VInst2(OP_RETURN, OPK_CONTROLTRANSFER, opr1, opr2);
            }

        protected:
            VInst2 (Opcode op, Opkind kind, Operand opr1, Operand opr2)
                : VInst(op, kind)
                {
                    operand[0] = opr1;
                    operand[1] = opr2;
                }

        private:
            Operand operand[2];
    };

    // VInst with THREE operands
    class VInst3 : public VInst {
        public:
            VInst3(Opcode op, Opkind kind, Operand opr1, Operand opr2, Operand opr3)
                : VInst(op, kind)
                {
                    operand[0] = opr1;
                    operand[1] = opr2;
                    operand[2] = opr3;
                }

            // Attention: only valid when i >= 1 && i <= 3, otherwise, undefined
            Operand getOperand(int i) const
            {
                return operand[i - 1];
            }

            void setOperand(int i, Operand oprnd)
            {
                operand[i - 1] = oprnd;
            }

            int getOperandNum() const { return 3; }

            // r1: source v-register number
            // size: block size, specifies which part of "r1" will be extended
            // r2: destination v-register number
            static VInst * createSEXT (XTRegNum r1, BlockSize size, XTRegNum r2)	
            {
                Operand opr1(Operand::VREG_USE), 
                        opr2(Operand::SIZE), 
                        opr3(Operand::VREG_DEF);

                opr1.reg = r1;
                opr2.size = size;
                opr3.reg = r2;
                return new VInst3 (OP_SEXT, OPK_COMPUTATION, opr1, opr2, opr3);
            }

            // ditto
            static VInst * createZEXT (XTRegNum r1, BlockSize size, XTRegNum r2)
            {
                Operand opr1(Operand::VREG_USE), 
                        opr2(Operand::SIZE), 
                        opr3(Operand::VREG_DEF);

                opr1.reg = r1;
                opr2.size = size;
                opr3.reg = r2;
                return new VInst3 (OP_ZEXT, OPK_COMPUTATION, opr1, opr2, opr3);
            }
            // ditto
            static VInst * createADDI( XTInt32 imm, XTRegNum r2, XTRegNum r3)
            {
                Operand opr1(Operand::IMM),
                        opr2(Operand::VREG_USE), 
                        opr3(Operand::VREG_DEF);

                opr1.imm = imm;
                opr2.reg= r2;
                opr3.reg = r3;
                return new VInst3 (OP_ADDI, OPK_COMPUTATION, opr1, opr2, opr3);
            }

#define CREATECOMP3(op)  \
            static VInst *create##op (XTRegNum r1, XTRegNum r2, XTRegNum r3)	\
            {	\
                Operand opr1(Operand::VREG_USE),    \
                opr2(Operand::VREG_USE),    \
                opr3(Operand::VREG_DEF);    \
                opr1.reg = r1;		    \
                opr2.reg = r2;		    \
                opr3.reg = r3;		    \
                return new VInst3 (OP_##op, OPK_COMPUTATION, opr1, opr2, opr3);	\
            }

            CREATECOMP3(ADD)
                CREATECOMP3(SUB)
                CREATECOMP3(AND)
                CREATECOMP3(XOR)
                CREATECOMP3(OR)
#undef CREATECOMP3

#define CREATESHIFT(op)								\
        static VInst *create##op (XTRegNum src, XTRegNum shi, XTRegNum dst)	\
{									\
    Operand opr1(Operand::VREG_USE),				\
    opr2(Operand::VREG_USE),				\
    opr3(Operand::VREG_DEF);				\
    opr1.reg = src;							\
    opr2.reg = shi;							\
    opr3.reg = dst;							\
    return new VInst3 (OP_##op, OPK_COMPUTATION, opr1, opr2, opr3);	\
}	

CREATESHIFT(SRL)
CREATESHIFT(SRA)
CREATESHIFT(SLL)

#undef CREATESHIFT

    private:
    Operand operand[3];
    };

// VInst with FOUR operands
class VInst4 : public VInst {
    public:

        // Attention: only valid when i >= 1 && i <= 4, otherwise, undefined
        Operand getOperand(int i) const
        {
            return operand[i - 1];
        }

        void setOperand(int i, Operand oprnd)
        {
            operand[i - 1] = oprnd;
        }

        int getOperandNum() const { return 4; }

        // (disp)reg: memory address
        // size: block size
        // dst: destination v-register number
        static VInst * createLD(XTRegNum reg, XTInt32 disp, BlockSize size, XTRegNum dst)
        {
            Operand opr1(Operand::VREG_USE),
                    opr2(Operand::IMM),
                    opr3(Operand::SIZE), 
                    opr4(Operand::VREG_DEF);

            opr1.reg = reg;
            opr2.imm = disp;
            opr3.size = size;
            opr4.reg = dst;
            return new VInst4(OP_LD, OPK_MEMORYACCESS, opr1, opr2, opr3, opr4);
        }


        // src: source v-register number
        // size: block size
        // (disp)reg: memory address
        static VInst * createST(XTRegNum src, BlockSize size, XTRegNum reg, XTInt32 disp)
        {
            Operand opr1(Operand::VREG_USE), 
                    opr2(Operand::SIZE), 
                    opr3(Operand::VREG_USE),
                    opr4(Operand::IMM);

            opr1.reg = src;
            opr2.size = size;
            opr3.reg = reg;
            opr4.imm = disp;
            return new VInst4(OP_ST, OPK_MEMORYACCESS, opr1, opr2, opr3, opr4);
        }

        // (disp)reg: memory address
        // size: block size
        // dst: destination v-register number
        static VInst * createFLD(XTRegNum reg, XTInt32 disp, BlockSize size, XTRegNum dst)
        {
            Operand opr1(Operand::VREG_USE),
                    opr2(Operand::IMM),
                    opr3(Operand::SIZE), 
                    opr4(Operand::FREG);

            opr1.reg = reg;
            opr2.imm = disp;
            opr3.size = size;
            opr4.reg = dst;
            return new VInst4(OP_FLD, OPK_MEMORYACCESS, opr1, opr2, opr3, opr4);
        }

        // src: source float-point register number
        // size: block size
        // (disp)reg: memory address
        static VInst * createFST(XTRegNum src, BlockSize size, XTRegNum reg, XTInt32 disp)
        {
            Operand opr1(Operand::FREG), 
                    opr2(Operand::SIZE), 
                    opr3(Operand::VREG_USE),
                    opr4(Operand::IMM);

            opr1.reg = src;
            opr2.size = size;
            opr3.reg = reg;
            opr4.imm = disp;
            return new VInst4(OP_FST, OPK_MEMORYACCESS, opr1, opr2, opr3, opr4);
        }

        // src: source float-point register number
        // srcsize: source data size
        // dst: destination float-point register number
        // dstsize: destination data size
        static VInst * createFCONV(XTRegNum src, BlockSize srcsize, XTRegNum dst, BlockSize dstsize)
        {
            Operand opr1(Operand::FREG),
                    opr2(Operand::SIZE),
                    opr3(Operand::FREG), 
                    opr4(Operand::SIZE);

            opr1.reg = src;
            opr2.size = srcsize;
            opr3.reg = dst;
            opr4.size = dstsize;
            return new VInst4(OP_FCONV, OPK_SPECIAL, opr1, opr2, opr3, opr4);
        }


#define CREATECOMP4(op)	\
        static VInst * create##op(XTRegNum r1, XTRegNum r2, XTRegNum r3, XTRegNum r4)	\
        {	    \
            Operand opr1(Operand::VREG_USE),    \
            opr2(Operand::VREG_USE),    \
            opr3(Operand::VREG_DEF),    \
            opr4(Operand::VREG_DEF);    \
            opr1.reg = r1;			\
            opr2.reg = r2;			\
            opr3.reg = r3;			\
            opr4.reg = r4;			\
            return new VInst4(OP_##op, OPK_COMPUTATION, opr1, opr2, opr3, opr4);	\
        }
        CREATECOMP4(MUL)
            CREATECOMP4(MULU)
            CREATECOMP4(DIV)
            CREATECOMP4(DIVU)
#undef CREATECOMP4

            // cc: compare condition code
            // r1: v-register number, left operand
            // r2: v-register number, right operand
            // r3: v-register number, where compare result (true/false) is stored
            static VInst * createCMP(CmpCC cc, XTRegNum r1, XTRegNum r2, XTRegNum r3)
            {
                Operand opr1(Operand::CC),    
                        opr2(Operand::VREG_USE),    
                        opr3(Operand::VREG_USE),
                        opr4(Operand::VREG_DEF); 

                opr1.cc = cc;
                opr2.reg = r1;
                opr3.reg = r2;
                opr4.reg = r3;
                return new VInst4(OP_CMP, OPK_COMPUTATION, opr1, opr2, opr3, opr4);
            }

        // cc: compare condition code
        // r1: float-point register number, left operand
        // r2: float-point register number, right operand
        // r3: float-point register number, where compare result (true/false) is stored
        static VInst * createFCMPS(CmpCC cc, XTRegNum r1, XTRegNum r2, XTRegNum r3)
        {
            Operand opr1(Operand::CC),    
                    opr2(Operand::FREG),    
                    opr3(Operand::FREG),
                    opr4(Operand::FREG); 

            opr1.cc = cc;
            opr2.reg = r1;
            opr3.reg = r2;
            opr4.reg = r3;
            return new VInst4(OP_FCMP_S, OPK_COMPUTATION, opr1, opr2, opr3, opr4);
        }

        // cc: compare condition code
        // r1: float-point register number, left operand
        // r2: float-point register number, right operand
        // r3: float-point register number, where compare result (true/false) is stored
        static VInst * createFCMPD(CmpCC cc, XTRegNum r1, XTRegNum r2, XTRegNum r3)
        {
            Operand opr1(Operand::CC),    
                    opr2(Operand::FREG),    
                    opr3(Operand::FREG),
                    opr4(Operand::FREG); 

            opr1.cc = cc;
            opr2.reg = r1;
            opr3.reg = r2;
            opr4.reg = r3;
            return new VInst4(OP_FCMP_D, OPK_COMPUTATION, opr1, opr2, opr3, opr4);
        }

    protected:
        VInst4 (Opcode op, Opkind kind, 
                Operand opr1, Operand opr2, Operand opr3, Operand opr4)
            : VInst(op, kind)
            {
                operand[0] = opr1;
                operand[1] = opr2;
                operand[2] = opr3;
                operand[3] = opr4;
            }

    private:
        Operand operand[4];
};

// VInst with FIVE operand
class VInst5 : public VInst {
    public:
        // Attention: only valide when i == 1, otherwise, undefined.
        Operand getOperand(int i) const
        {
            return operand[i - 1];
        }

        void setOperand(int i, Operand oprnd)
        {
            operand[i - 1] = oprnd;
        }

        int getOperandNum() const { return 5; }

        static VInst * createBRANCH(CmpCC cc, XTRegNum r1, XTRegNum r2, 
                XTRegNum reg, XTInt32 disp)
        {
            Operand opr1(Operand::CC),
            opr2(Operand::VREG_USE),
            opr3(Operand::VREG_USE),
            opr4(Operand::VREG_USE),
            opr5(Operand::IMM);

            opr1.cc = cc;
            opr2.reg = r1;
            opr3.reg = r2;
            opr4.reg = reg;
            opr5.imm = disp;
            return new VInst5(OP_BRANCH, OPK_CONTROLTRANSFER, 
                    opr1, opr2, opr3, opr4, opr5);
        }

    protected:
        VInst5(Opcode op, Opkind kind, 
                Operand opr1, Operand opr2, Operand opr3, Operand opr4, Operand opr5) 
            :VInst(op, kind)
            {
                operand[0] = opr1;
                operand[1] = opr2;
                operand[2] = opr3;
                operand[3] = opr4;
                operand[4] = opr5;
            }

    private:
        Operand operand[5];
};


class VInstVar : public VInst {
    public:

        Operand getOperand(int i) const
        {
            return operand[i - 1];
        }

        void setOperand(int i, Operand oprnd)
        {
            operand[i - 1] = oprnd;
        }

        int getOperandNum() const
        {
            return operand.size();
        }

        static VInst * createCALL (XTInt32 disp, int n, ...)
        {
            VInstVar *inst = new VInstVar(OP_CALL, OPK_SPECIAL);		

            Operand func(Operand::WORD), 
                    argN(Operand::WORD), 
                    arg(Operand::WORD);

            func.word = disp;
            argN.word = n;

            inst->operand.push_back(func);
            inst->operand.push_back(argN);

            // push all the function arguments
            va_list ap;
            va_start(ap, n);
            for (int i = 0; i < n; i++) {
                arg.word = va_arg(ap, XTInt32);
                inst->operand.push_back(arg);
            }
            va_end(ap);

            return inst;
        }

    protected:
        VInstVar (Opcode op, Opkind kind)
            : VInst(op, kind)
            {
            }

    private:
        std::vector<Operand> operand;
};

}

#endif
